package com.cndcare.msc.handler;

import com.cndcare.msc.protobuf.CmdEnum;
import com.cndcare.msc.protobuf.MessageVo;
import com.cndcare.msc.util.IPUtils;
import com.cndcare.msc.util.LocalMac;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoop;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
public class HeartHandler extends SimpleChannelInboundHandler<MessageVo> {

    private NettyClient nettyClient;
    private int heartbeatCount = 0;
    private long ccTime = 0;//缓存发送时间 单位毫秒

    // 定义客户端没有收到服务端的pong消息的最大次数
    public static int MAX_UN_REC_PONG_TIMES = 5;

    // 多长时间未请求后，发送心跳
    private static final int WRITE_WAIT_SECONDS = 5;//暂时未使用

    // 隔N秒后重连
    private static final int RE_CONN_WAIT_SECONDS = 5;//暂时未使用

    // 客户端连续N次没有收到服务端的pong消息  计数器
    private int unRecPongTimes = 0;

    /**
     * @param nettyClient
     */
    public HeartHandler(NettyClient nettyClient) {
        this.nettyClient = nettyClient;
    }

    // 连接成功后，向server发送消息
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        MessageVo messageVo = MessageVo.builder()
                .mac(LocalMac.getLocalMac())
                .ip(IPUtils.getRealIp())
                .cmdEnum(CmdEnum.AUTH)
                .data("This is auth data").build();
        log.info("messageVo = {}", messageVo);

        ClientChannel.ctx = ctx;

        ctx.writeAndFlush(messageVo);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            String type = "";
            if (event.state() == IdleState.READER_IDLE) {
                type = "read idle";

                if (unRecPongTimes < MAX_UN_REC_PONG_TIMES) {
                    sendPingMsg(ctx, LocalMac.getLocalMac());

                    log.info("unRecPongTimes = {}", unRecPongTimes);
                    unRecPongTimes++;
                } else {
                    log.info("重试次数上限{}，自动关闭了频道 type = {}", type, MAX_UN_REC_PONG_TIMES);
                    ctx.channel().close();
                }
            } else if (event.state() == IdleState.WRITER_IDLE) {
                type = "write idle";
            } else if (event.state() == IdleState.ALL_IDLE) {
                type = "all idle";
            }
            log.info("type = {}", type);
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    /**
     * 发送ping消息
     *
     * @param context
     */
    protected void sendPingMsg(ChannelHandlerContext context, String mac) {
        heartbeatCount++;
        ccTime = System.currentTimeMillis();
        context.writeAndFlush(
                MessageVo.builder()
                        .mac(mac)
                        .ip(IPUtils.getRealIp())
                        .cmdEnum(CmdEnum.PING)
                        .data(String.valueOf(ccTime))
                        .build()
        );

//        log.info("Client sent ping msg to " + context.channel().remoteAddress() + ", count: " + heartbeatCount);
    }

    /**
     * @return返回微秒 如果有更深一步的业务可使用
     */
    public static Long getmicTime() {
        Long cutime = System.currentTimeMillis() * 1000; // 微秒
        Long nanoTime = System.nanoTime(); // 纳秒
        return cutime + (nanoTime - nanoTime / 1000000 * 1000000) / 1000;
    }

    /**
     * 处理断开重连
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("检测到心跳服务器断开！！！");
        final EventLoop eventLoop = ctx.channel().eventLoop();
        eventLoop.schedule(() -> nettyClient.doConnect(new Bootstrap(), eventLoop), 10L, TimeUnit.SECONDS);
        super.channelInactive(ctx);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MessageVo messageVo) throws Exception {
        if (messageVo.getCmdEnum().equals(CmdEnum.AUTH_BACK)) {
            log.info(messageVo.getData());
            ctx.writeAndFlush(
                    MessageVo.builder()
                            .mac(LocalMac.getLocalMac())
                            .ip(IPUtils.getRealIp())
                            .cmdEnum(CmdEnum.PUSH_DATA)
                            .data("发送业务数据中。。。")
                            .build()
            );
        } else if (messageVo.getCmdEnum().equals(CmdEnum.PING)) {
            //接收到server发送的ping指令
            log.info("服务端发来的消息：{}", messageVo.getData());

        } else if (messageVo.getCmdEnum().equals(CmdEnum.PONG)) {
            //接收到server发送的pong指令
            unRecPongTimes = 0;
            //计算ping值
            long ping = (System.currentTimeMillis() - ccTime) / 2;
            log.info("客户端和服务器的ping是" + ping + "ms");
        } else if (messageVo.getCmdEnum().equals(CmdEnum.PUSH_DATA)) {
            //接收到server推送数据
            log.info("接收到server推送数据: {}", messageVo.getData());
        } else if (messageVo.getCmdEnum().equals(CmdEnum.PUSH_DATA_BACK)) {
            //接收到server返回数据
            log.info("接收到server返回数据: {}", messageVo.getData());
        } else {
            log.info("其他数据: {}", messageVo.getData());
        }
    }

}
